<!--
 * @Descripttion: 课堂分析详情-文字看板
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-10-22 14:20:02
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-10-22 14:20:02
-->
<template>
	<div class="course-text-wide-wrapper" :class="{ 'is-empty': !textBoardData.length }">
		<div v-if="hasData" class="title-search">
			<!-- 文本替换 -->
			<div v-if="false" class="text-replacement">
				<img src="@/assets/images/lessonAnalysisDetails/text-replacement.png" />
				<p>文本替换</p>
			</div>
			<!-- 搜索框 -->
			<div class="search-wrap">
				<el-input
					v-model="searchValue"
					placeholder="输入检索内容"
					class="search-input"
					@change="onSearchTextBoardHandle"
				></el-input>
				<img
					src="@/assets/images/search-icon.png"
					class="search-icon"
					alt=""
					@click="onSearchTextBoardHandle(searchValue)"
				/>
			</div>
		</div>
		<!-- 文字看板列表 -->
		<div v-if="textBoardData.length" class="b-speak-wrap" @scroll="scrollTextBoard">
			<div v-for="(item, index) in chapterTextBoardList" :key="index" class="b-course-text">
				<div class="t-text-time">
					<span class="time-text">{{ formatSeconds(item.startTime / 1000) }}</span>
					<span v-if="item.mainClass === 'lecture'" class="speaker">老师</span>
					<img v-if="false" src="@/assets/images/lessonAnalysisDetails/text-edit.png" />
				</div>
				<div :class="{ 't-text-content': true }">
					<span
						v-for="item2 in item.list"
						:key="item2.bg"
						:class="{
							'text-item': true,
							active:
								Number.isFinite(item2.oriBg) &&
								Number.isFinite(item2.oriEd) &&
								currentTime >= (item2.oriBg as number) &&
								currentTime < (item2.oriEd as number)
						}"
						@click="clickWordChangeVideo((item2.oriBg as number) / 1000)"
						v-html="item2.onebest"
					>
					</span>
				</div>
			</div>
		</div>
		<!-- 暂无资源 -->
		<div v-if="!textBoardData.length" class="empty-wrap">暂无内容</div>
	</div>
</template>

<script lang="ts" setup>
import { formatSeconds } from '@/assets/js/utils';
import { ref, watch, onMounted } from 'vue';
import { getErrorMessage, getTextBoardAPI, getAiChapterAllAPI } from '@/api/index';
import type {
	TextBoard,
	ChapterListItemType,
	ChapterTextBoardListItemType
} from './CourseTextWide.type';

const props = withDefaults(
	defineProps<{
		currentTime: number;
		lessonCode: string;
	}>(),
	{
		currentTime: 0,
		lessonCode: ''
	}
);

const emit = defineEmits<{
	(e: 'changeVideoTime', time: number): void; // 切换视频时间
}>();

onMounted(() => {
	getAiChapter();
});

// 章节信息
const chapterList = ref<ChapterListItemType[]>([]);
// 获取章节
const getAiChapter = async () => {
	try {
		const data = await getAiChapterAllAPI({ businessCode: props.lessonCode });
		chapterList.value = data?.list || [];
	} catch (error) {
		console.error(getErrorMessage(error));
	} finally {
		// 获取文字看板
		getTextBoardData();
	}
};

// 模糊搜索
const searchValue = ref('');
// 自动滚动标识
const autoPlayFlag = ref(true);
// 文字看板原始数据
const textBoardData = ref<TextBoard[]>([]);
// 处理后的文字看板数据
const resultTextBoardDataArr = ref<TextBoard[]>([]);
// 处理后的文字看板数据Map对象
const textResultData = ref<Record<string, TextBoard>>({});
// 融合章节的文字看板列表
const chapterTextBoardList = ref<ChapterTextBoardListItemType[]>([]);
// 是否有数据
const hasData = ref(false);
// 是否从图表中搜索
const fromChart = ref(false);

// 获取文字看板
const getTextBoardData = async () => {
	const params = {
		businessCode: props.lessonCode,
		businessType: 1,
		isFilter: 0,
		searchValue: searchValue.value
	};

	try {
		const data = await getTextBoardAPI(params);

		if (searchValue.value && !fromChart.value) {
			autoPlayFlag.value = false;
		} else {
			autoPlayFlag.value = true;
		}

		if (!searchValue.value && data?.length) {
			hasData.value = true;
		}

		textBoardData.value = data;

		if (data?.length) {
			sortTextResultData();

			// 章节时间与文字看板时间合并
			const textBoardList = Object.values(textResultData.value);

			if (chapterList.value.length && textBoardList.length) {
				// 合并时间
				const chapterSecoundList = JSON.parse(JSON.stringify(chapterList.value)).map(
					(item: ChapterListItemType) => {
						item.oriStartTime = item.startTime;
						item.oriEndTime = item.endTime;
						return item;
					}
				);

				chapterTextBoardList.value = mergeChapterAndBoardTime(chapterSecoundList, textBoardList);
			} else if (!chapterList.value.length && textBoardList.length) {
				chapterTextBoardList.value = mergeChapterAndBoardTime([], textBoardList);
			}

			// 如果从图表中搜索，则视频自动跳转到第一个文字看板
			if (fromChart.value && chapterTextBoardList.value?.length) {
				emit('changeVideoTime', chapterTextBoardList.value[0].startTime / 1000);
			}
		}
	} catch (error) {
		textBoardData.value = [];
		console.error(getErrorMessage(error));
	}
};

const sortTextResultData = () => {
	// 创建一个空对象来保存处理后的结果
	const obj: Record<string, TextBoard> = {};

	// 创建一个空数组来保存过滤后的数据
	const filteredTextBoardData = [];

	let i = 0;
	let j = 0;

	// 使用 while 循环遍历 textBoardData 数组
	while (i < textBoardData.value.length) {
		// 获取当前元素
		const currentEntry = textBoardData.value[i];
		currentEntry.oriBg = Number(currentEntry.bg);
		currentEntry.oriEd = Number(currentEntry.ed);

		// 将当前元素的 bg 属性除以 1000 并向下取整，得到 key
		// const key = Math.floor(currentEntry.bg / 1000);
		// currentEntry.ed = Math.floor(currentEntry.ed / 1000);
		const key = currentEntry.bg;

		// 如果下一条数据存在，并且它的 bg 属性与当前元素的 bg 属性在同一秒内
		// 则将下一条数据的 onebest 属性值添加到当前元素的 onebest 属性值后
		// 并将下标 i 自增 1，继续检查下一条数据
		// while (
		// 	this.textBoardData[i + 1] &&
		// 	key === Math.floor(this.textBoardData[i + 1].bg / 1000)
		// ) {
		// 	currentEntry.onebest += this.textBoardData[i + 1].onebest;
		// 	i += 1;
		// }

		// 将处理后的当前元素添加到过滤后的数据数组中
		filteredTextBoardData.push(currentEntry);

		// 将处理后的当前元素添加到结果对象中，key 为其 bg 属性除以 1000 后的值
		obj[key] = currentEntry;

		// 为结果对象中的当前元素添加 index 属性，值为当前下标 i
		obj[key].index = j;

		// 将结果对象中的当前元素的 bg 属性更新为 key
		obj[key].bg = key;

		// 将下标 i 自增 1，继续处理下一条数据
		i += 1;
		// j的数值不受i影响，始终是连续的
		j += 1;
	}

	// 创建 resultTextBoardDataArr 属性，并复制过滤后的数据数组的值
	resultTextBoardDataArr.value = [...filteredTextBoardData];

	// 创建 textResultData 属性，并复制结果对象的值
	textResultData.value = { ...obj };
};

// 章节时间与文字看板时间合并
const mergeChapterAndBoardTime = (
	chapterSecoundList: ChapterListItemType[],
	textBoardList: TextBoard[]
): ChapterTextBoardListItemType[] => {
	const chapterBoardTimeList: ChapterTextBoardListItemType[] = [];

	// 遍历章节时间列表
	if (chapterSecoundList.length) {
		chapterSecoundList.forEach((block: ChapterListItemType) => {
			// 创建一个新的对象用于存储合并后的句子信息
			const mergedSentence: ChapterTextBoardListItemType = {
				startTime: block.startTime,
				endTime: block.endTime,
				lessonId: null, // 初始化 lessonId
				onebest: '', // 初始化 onebest
				speaker: null, // 初始化 speaker
				list: [],
				// 判断当前片段类型
				mainClass: null
			};

			mergedSentence.mainClass = block.mainClass;

			// 遍历文字看板时间列表
			textBoardList.forEach((sentence) => {
				const sentenceStartTime = Math.floor(sentence.oriBg || 0);

				// 判断当前句子是否属于当前时间块
				if (
					Number.isFinite(block.oriStartTime) &&
					Number.isFinite(block.oriEndTime) &&
					sentenceStartTime >= (block.oriStartTime as number) &&
					sentenceStartTime < (block.oriEndTime as number)
				) {
					// 如果是，则将句子内容添加到 mergedSentence.onebest 中
					mergedSentence.onebest += sentence.onebest;
					mergedSentence.list.push(sentence);
					// 更新 lessonId 和 speaker（如果还没有设置）
					if (!mergedSentence.lessonId) mergedSentence.lessonId = sentence.lessonId;
					if (!mergedSentence.speaker) mergedSentence.speaker = sentence.speaker;
				}
			});

			// 将合并后的句子信息添加到数组中
			if (mergedSentence.onebest) {
				// 只有当 onebest 不为空时才添加到结果数组
				chapterBoardTimeList.push(mergedSentence);
			}
		});
	} else {
		// 无章节时，直接用 textBoardList 构造 chapterBoardTimeList 格式
		textBoardList.forEach((sentence) => {
			const item = {
				startTime: parseInt(sentence.bg, 10),
				endTime: parseInt(sentence.ed, 10),
				lessonId: null,
				speaker: sentence.speaker,
				mainClass: '',
				onebest: sentence.onebest,
				list: [sentence] // 自身作为 list
			};
			chapterBoardTimeList.push(item);
		});
	}

	return chapterBoardTimeList;
};

// 搜索关键字词
const onSearchTextBoardHandle = (val: string, _fromChart = false) => {
	fromChart.value = _fromChart;
	searchValue.value = val;
	autoPlayFlag.value = _fromChart || !searchValue.value;
	getTextBoardData();
};

// 点击跳转到视频对应时间节点
const clickWordChangeVideo = (time: number) => {
	if (Number.isFinite(time)) {
		autoPlayFlag.value = true;
		emit('changeVideoTime', time);
	}
};

/** 文字看板自动滚动相关 */
// 文字看板自动滚动
const autoPlay = () => {
	// 文字看板容器
	const container: HTMLDivElement | null = document.querySelector('.b-speak-wrap');
	// 可视高度
	const containerHeight = container?.clientHeight || 0;
	// 高亮文本
	const activeItem: HTMLDivElement | null = document.querySelector('.text-item.active');

	if (container && activeItem) {
		// 高亮文本距离容器上边的距离
		const itemTop = activeItem.offsetTop;
		// 高亮文本的高度
		const itemHeight = activeItem.offsetHeight;

		container.scrollTop = itemTop - containerHeight / 2 + itemHeight / 2 - 40;
	}

	// 解决自动滚动时会触发scroll事件
	setTimeout(() => {
		autoPlayFlag.value = true;
	}, 100);
};

// 滚动定时器
const scrollTimeout = ref<ReturnType<typeof setTimeout> | null>(null);
// 监听滚轮滑动 停止自动滚动
const scrollTextBoard = (e: Event) => {
	if (e) {
		if (scrollTimeout.value) {
			clearTimeout(scrollTimeout.value);
		}
		autoPlayFlag.value = false;
	}
	// 用户在30秒内没有再次滑动滚轮，则继续自动滚动
	scrollTimeout.value = setTimeout(() => {
		autoPlayFlag.value = true;
	}, 30 * 1000);
};

// 设置autoPlayFlag
const setAutoPlayFlag = (flag: boolean) => {
	autoPlayFlag.value = flag;
};

defineExpose({
	setAutoPlayFlag,
	onSearchTextBoardHandle
});

watch(
	() => props.currentTime,
	() => {
		if (Object.keys(textResultData.value).length && autoPlayFlag.value) {
			autoPlay();
		}
	},
	{ deep: true, immediate: true }
);
</script>

<style scoped lang="scss">
.course-text-wide-wrapper {
	box-sizing: border-box;
	width: 100%;
	height: 100%;
	position: relative;

	&.is-empty {
		padding-right: 0.7rem;
	}

	.title-search {
		display: flex;
		align-items: center;
		flex-direction: row-reverse;
		gap: 1rem;
		padding-right: 0.7rem;

		.title {
			color: #fff;
			font-size: $font-size-base;
		}

		.search-wrap {
			position: relative;
			display: flex;
			align-items: center;
			border-radius: 0.2rem;

			:deep(.el-input__inner) {
				padding-right: 3rem;
				width: 30.6rem;
			}

			// 搜索图标
			.search-icon {
				position: absolute;
				top: 50%;
				right: 1rem;
				width: $font-size-base;
				height: $font-size-base;
				transform: translateY(-50%);
				cursor: pointer;
			}
		}

		.text-replacement {
			display: flex;
			align-items: center;
			gap: 0.3rem;
			color: $color-brand;
			cursor: pointer;

			img {
				width: $font-size-base;
				height: $font-size-base;
			}
		}
	}

	// 文字看板列表
	.b-speak-wrap {
		height: calc(100% - 3rem - 0.5rem);
		overflow: auto;
		margin-top: 0.5rem;

		// 每一条文字看板
		.b-course-text {
			cursor: pointer;
			color: $text-color-regular;

			&:first-child {
				.t-text-time {
					line-height: calc(3rem + 2px);
				}
			}

			.t-text-time {
				font-size: $font-size-small-1;
				line-height: $line-height-lg;
				display: flex;
				align-items: center;
				margin-bottom: 0.6rem;
				font-size: $font-size-base;

				.speaker {
					margin-left: 0.5rem;
				}

				img {
					margin-left: 1rem;
					width: $font-size-base;
					height: $font-size-base;
					cursor: pointer;
				}
			}

			.t-text-content {
				background: #fff;
				padding: 1rem;
				border-radius: 0.4rem;
				font-size: $font-size-small-1;
				line-height: $line-height-lg;

				// 搜索高亮词
				:deep(mark) {
					background: none;
					color: $color-brand;
				}

				.text-item:hover {
					color: #000;
					cursor: pointer;
				}
			}
		}

		.b-course-text + .b-course-text {
			margin-top: 2rem;
		}

		.b-course-text.active {
			color: $color-brand;
		}
	}

	// 文字看板高亮颜色
	.text-item.active {
		color: $color-brand;
	}
}
</style>
